﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace PSE.Framework.Threading
{
    /// <summary>
    /// Provides method to sleep a thread in a fractionally way.
    /// </summary>
    public class Sleeper
    {
        /// <summary>
        /// Represents the method that will define the condition to the sleep to be aborted.
        /// </summary>
        /// <returns></returns>
        public delegate bool HandlerToAbortSleep();

        /// <summary>
        /// Suspends the current thread for a specified time.
        /// </summary>
        /// <param name="millisecondsTimeout">The number of milliseconds for which the thread is blocked. Specify zero
        /// (0) to indicate that this thread should be suspended to allow other waiting
        /// threads to execute. Specify System.Threading.Timeout.Infinite to block the
        /// thread indefinitely.</param>
        /// <param name="handlerToAbortSleep">The method that will define the condition to the sleep to be aborted.</param>
        public static void Sleep(int millisecondsTimeout, HandlerToAbortSleep handlerToAbortSleep)
        {
            if (millisecondsTimeout < 0 && millisecondsTimeout != System.Threading.Timeout.Infinite)
                throw new ArgumentOutOfRangeException("millisecondsTimeout", "The time-out value is negative and is not equal to System.Threading.Timeout.Infinite.");

            if (handlerToAbortSleep == null)
                throw new ArgumentNullException("handlerToAbortSleep");

            new SleepCaps().SleepInten(millisecondsTimeout, handlerToAbortSleep);
        }

        private class SleepCaps
        {
            public void SleepInten(int milliseconds, HandlerToAbortSleep handlerToAbortSleep)
            {
                const int MAX_TIME_TO_THREAD_SLEEP = 1000;

                int totalTimeToSleep = milliseconds;
                int fractionedTimeToSleep;
                int timeSlept;

                if (totalTimeToSleep <= 0)
                    throw new ArgumentOutOfRangeException("The milliseconds parameter must be greater than 0.");

                fractionedTimeToSleep = totalTimeToSleep % MAX_TIME_TO_THREAD_SLEEP;

                timeSlept = 0;
                while ((timeSlept < totalTimeToSleep) && handlerToAbortSleep())
                {
                    if (fractionedTimeToSleep > 0)
                    {
                        Thread.Sleep(fractionedTimeToSleep);
                        timeSlept += fractionedTimeToSleep;
                        fractionedTimeToSleep = 0;
                    }
                    else
                    {
                        Thread.Sleep(MAX_TIME_TO_THREAD_SLEEP);
                        timeSlept += MAX_TIME_TO_THREAD_SLEEP;
                    }
                }
            }
        }
    }
}
